﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ClickerClimber.Model;
using ClickerClimber.Strategy;
using ClickerClimber.Managers;

namespace ClickerClimber.Strategy
{
    public class GameStrategy : IStrategy
    {
        private Random _randomGenerator = new Random();
        private Array _itemTypes = Enum.GetValues(typeof(Item.ItemType));
        private Array _itemBackgroundTypes = Enum.GetValues(typeof(Item.ItemBackgroundType));

        private int _lastTurnWaterRaise = 0;
        
        public bool FindPattern(Item[,] board, Item root, out List<Pair> pattern, bool score)
        {
            pattern = new List<Pair>();

            if (!root.HasItem) return false;

            foreach(Item item in board)
            {
                if ((item != null) && (item.HasItem))
                {
                    item.Visited = false;
                }
            }
            
            pattern.Add(root.CellPosition);

            List<Pair> newPattern = FindPatternRecursive(board, root);

            pattern.AddRange(newPattern);

            if (score)
            {
                UpdatePoints(pattern.Count);
            }

            return pattern.Count>2;
        }

        private List<Pair> FindPatternRecursive(Item[,] board, Item root)
        {
            List<Pair> pattern = new List<Pair>();
            root.Visited = true;

            List<Item> neighbours = GetNeighbours(board, root);
            foreach (Item neighbour in neighbours)
            {
                List<Pair> newPattern;
                if (neighbour.Type == root.Type && !neighbour.Visited & neighbour.HasItem)
                {
                    newPattern = FindPatternRecursive(board, neighbour);
                    pattern.Add(neighbour.CellPosition);
                    pattern.AddRange(newPattern);
                }
            }
            return pattern;
        }

        public Item.ItemType GetRandomItemType()
        {
            int numItems = GameParameters.NUM_ITEMS;
            if (numItems > _itemTypes.Length)
                numItems = _itemTypes.Length;

            return (Item.ItemType)_itemTypes.GetValue(_randomGenerator.Next(numItems));
        }

        public Item.ItemBackgroundType GetRandomItemBackgroundType()
        {
            return (Item.ItemBackgroundType)_itemBackgroundTypes.GetValue(_randomGenerator.Next(_itemBackgroundTypes.Length));
        }

        private List<Item> GetNeighbours(Item[,] board, Item root)
        {
            List<Item> neighbours = new List<Item>();

            if (root.CellPosition.Column - 1 >= 0 && board[root.CellPosition.Column - 1, root.CellPosition.Row] != null )
            {
                neighbours.Add(board[root.CellPosition.Column - 1, root.CellPosition.Row]);
            }
            if (root.CellPosition.Column+1 <= GameParameters.BOARD_BASE_COLUMNS-1 && 
                board[root.CellPosition.Column + 1, root.CellPosition.Row] != null)
            {
                neighbours.Add(board[root.CellPosition.Column + 1, root.CellPosition.Row]);
            }
            if (root.CellPosition.Row - 1 >= 0 && board[root.CellPosition.Column, root.CellPosition.Row-1] != null)
            {
                neighbours.Add(board[root.CellPosition.Column, root.CellPosition.Row-1]);
            }
            if (root.CellPosition.Row +1 <= GameParameters.BOARD_BASE_ROWS-1 && 
                board[root.CellPosition.Column, root.CellPosition.Row+1] != null)
            {
                neighbours.Add(board[root.CellPosition.Column, root.CellPosition.Row+1]);
            }

            return neighbours;
        }

        public List<Pair> GetPositionsToFill(int nbPos, Item[,] board)
        {
            List<Pair> listEmpty = new List<Pair>();
            List<Pair> listToFill = new List<Pair>();
            for (int r = 0; r < GameParameters.BOARD_BASE_ROWS; r++)
            {
                for (int c = 0; c < GameParameters.BOARD_BASE_COLUMNS; c++)
                {
                    if ((board[c, r] == null) || !board[c, r].HasBackground)
                    {
                        Pair item = new Pair(c,r);
                        listEmpty.Add(item);
                    }
                }

                if (listEmpty.Count > 0)
                {
                    if (listEmpty.Count < nbPos)
                    {
                        foreach (Pair pair in listEmpty)
                        {
                            listToFill.Add(pair);
                            nbPos--;
                        }
                    }
                    else
                    {
                        while (nbPos > 0)
                        {
                            int iPos = _randomGenerator.Next() % listEmpty.Count;
                            listToFill.Add(listEmpty[iPos]);
                            listEmpty.RemoveAt(iPos);
                            nbPos--;
                        }
                        return listToFill;
                    }
                }
            }

            return listToFill;
        }

        private void UpdatePoints(int patternLength)
        {
            switch (patternLength)
            {
                case 3:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE3);
                    break;
                case 4:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE4);
                    break;
                case 5:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE5);
                    break;
                case 6:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE6);
                    break;
                case 7:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE7);
                    break;
                case 8:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE8);
                    break;
                case 9:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE9);
                    break;
                case 10:
                    DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVE10);
                    break;
                default:
                    if (patternLength > 2)
                    {
                        DataManager.Instance.HUDScene.AddScore(GameParameters.POINTS_REMOVEMAX);
                    }
                    break;
            }
        }

        public List<Pair> GetPositionsToDelete(int nbPos, Item[,] board, int maxheight)
        {
            List<Pair> listFilled = new List<Pair>(); 
            List<Pair> listToFill = new List<Pair>();

            int iMaxHeight = maxheight - 1;
            int iMinHeight = iMaxHeight - 5;
            if (iMinHeight < 0)
                iMinHeight = 0;
            for (int r = iMinHeight; r < iMaxHeight; r++)
            {
                for (int c = 0; c < GameParameters.BOARD_BASE_COLUMNS; c++)
                {
                    if ((board[c, r] != null) && board[c, r].HasBackground)
                    {
                        Pair item = new Pair(c, r);
                        listFilled.Add(item);
                    }
                }
            }
            while ((nbPos > 0) && (listFilled.Count > 0))
            {
                int iPos = _randomGenerator.Next() % listFilled.Count;
                listToFill.Add(listFilled[iPos]);
                listFilled.RemoveAt(iPos);
                nbPos--;
            }
            return listToFill;
        }

        public int WaterRaise(int currentTurn, int seconds)
        {
            int turnsPassed = currentTurn -_lastTurnWaterRaise;
            int secondsPassed = GameParameters.GAME_TIME - seconds; 
            int waterRaise = 0;

            if (secondsPassed >= GameParameters.WATER_PHASE_3_TIME && turnsPassed > 0)
            {
                waterRaise = GameParameters.WATER_INCREASE_PHASE_3;
                _lastTurnWaterRaise = currentTurn;
            }
            else if (secondsPassed >= GameParameters.WATER_PHASE_2_TIME && turnsPassed > 1)
            {
                waterRaise = GameParameters.WATER_INCREASE_PHASE_2;
                _lastTurnWaterRaise = currentTurn;
            }
            else if (secondsPassed >= GameParameters.WATER_PHASE_1_TIME && turnsPassed > 2)
            {
                waterRaise = GameParameters.WATER_INCREASE_PHASE_1;
                _lastTurnWaterRaise = currentTurn;
            }

            return waterRaise;
        }
    }
}
